#include "stdafx.h"
#include "net/HTTPChunkedStream.hpp"
#include "utils/numberparser.hpp"
#include "utils/numberformatter.hpp"
using JHCPP::utils::CNumberParser;
using JHCPP::utils::CNumberFormatter;
#include "charset/ascii.hpp"
using JHCPP::charset::CAscii;

NET_NAMESPACE_BEGIN

//
// CHTTPChunkedStreamBuf
//
CHTTPChunkedStreamBuf::CHTTPChunkedStreamBuf(CHTTPSession& session, openmode mode)
	: HTTPBasicStreamBuf(CHTTPBufferAllocator::BUFFER_SIZE, mode),
	m_session(session),
	m_mode(mode),
	m_chunk(0)
{
}

CHTTPChunkedStreamBuf::~CHTTPChunkedStreamBuf()
{
}

void CHTTPChunkedStreamBuf::close()
{
	if (m_mode & std::ios::out)
	{
		sync();
		m_session.write("0\r\n\r\n", 5);
	}
}

int CHTTPChunkedStreamBuf::readFromDevice(char* buffer, std::streamsize length)
{
	static const int eof = std::char_traits<char>::eof();

	if (m_chunk == 0)
	{
		int ch = m_session.get();
		while (CAscii::isSpace(ch)) ch = m_session.get();
		std::string chunkLen;
		while (CAscii::isHexDigit(ch)) { chunkLen += (char) ch; ch = m_session.get(); }
		while (ch != eof && ch != '\n') ch = m_session.get();
		unsigned chunk;
		if (CNumberParser::tryParseHex(chunkLen, chunk))
			m_chunk = (std::streamsize) chunk;
		else
			return eof;
	}
	if (m_chunk > 0)
	{
		if (length > m_chunk) length = m_chunk;
		int n = m_session.read(buffer, length);
		if (n > 0) m_chunk -= n;
		return n;
	}
	else 
	{
		int ch = m_session.get();
		while (ch != eof && ch != '\n') ch = m_session.get();
		return 0;
	}
}

int CHTTPChunkedStreamBuf::writeToDevice(const char* buffer, std::streamsize length)
{
	m_chunkBuffer.clear();
	CNumberFormatter::appendHex(m_chunkBuffer, length);
	m_chunkBuffer.append("\r\n", 2);
	m_chunkBuffer.append(buffer, static_cast<std::string::size_type>(length));
	m_chunkBuffer.append("\r\n", 2);
	m_session.write(m_chunkBuffer.data(), static_cast<std::streamsize>(m_chunkBuffer.size()));
	return static_cast<int>(length);
}


//
// CHTTPChunkedIOS
//
CHTTPChunkedIOS::CHTTPChunkedIOS(CHTTPSession& session, CHTTPChunkedStreamBuf::openmode mode) : m_buf(session, mode)
{
	
}

CHTTPChunkedIOS::~CHTTPChunkedIOS()
{
	try
	{
		m_buf.close();
	}
	catch (...)
	{
	}
}


CHTTPChunkedStreamBuf* CHTTPChunkedIOS::rdbuf()
{
	return &m_buf;
}


//
// HTTPChunkedInputStream
//
CMemoryPool CHTTPChunkedInputStream::m_pool(sizeof(CHTTPChunkedInputStream));

CHTTPChunkedInputStream::CHTTPChunkedInputStream(CHTTPSession& session)
	: CHTTPChunkedIOS(session, std::ios::in),
	std::istream(&m_buf)
{
}

CHTTPChunkedInputStream::~CHTTPChunkedInputStream()
{
}

void* CHTTPChunkedInputStream::operator new(std::size_t size)
{
	return m_pool.get();
}

void CHTTPChunkedInputStream::operator delete(void* ptr)
{
	try
	{
		m_pool.release(ptr);
	}
	catch (...)
	{
		jh_unexpected();
	}
}


//
// CHTTPChunkedOutputStream
//
CMemoryPool CHTTPChunkedOutputStream::m_pool(sizeof(CHTTPChunkedOutputStream));

CHTTPChunkedOutputStream::CHTTPChunkedOutputStream(CHTTPSession& session)
	: CHTTPChunkedIOS(session, std::ios::out),
	std::ostream(&m_buf)
{
}

CHTTPChunkedOutputStream::~CHTTPChunkedOutputStream()
{
}

void* CHTTPChunkedOutputStream::operator new(std::size_t size)
{
	return m_pool.get();
}

void CHTTPChunkedOutputStream::operator delete(void* ptr)
{
	try
	{
		m_pool.release(ptr);
	}
	catch (...)
	{
		jh_unexpected();
	}
}


NET_NAMESPACE_END
